package com.yiji.fintech.musfinance.facadeimpl.withdraw;

import com.acooly.core.utils.Ids;
import com.acooly.core.utils.Strings;
import com.acooly.core.utils.mapper.BeanCopier;
import com.acooly.module.scheduler.api.ScheduleCallBackService;
import com.alibaba.dubbo.config.annotation.Service;
import com.google.common.collect.Sets;
import com.yiji.fintech.musfinance.base.enums.NotifyStatusEnum;
import com.yiji.fintech.musfinance.base.enums.TransStatusEnum;
import com.yiji.fintech.musfinance.center.withdraw.result.WithdrawResult;
import com.yiji.fintech.musfinance.common.domain.WithdrawDomain;
import com.yiji.fintech.musfinance.common.enums.ReviewStatusEnum;
import com.yiji.fintech.musfinance.common.service.ThreadPoolService;
import com.yiji.fintech.musfinance.common.strategy.gateway.SendGateWayWithdrawStrategy;
import com.yiji.fintech.musfinance.common.utils.cache.DistributeCacheService;
import com.yiji.fintech.musfinance.common.utils.cache.CacheConstant;
import com.yiji.fintech.musfinance.entity.TransactionWithdraw;
import com.yiji.fintech.musfinance.service.TransactionWithdrawService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;

/**
 * Created by ouwen@yiji.com} on 2017/6/14.
 */
@Service(version = "2.0",group = "com.yiji.fintech.musfinance.center.withdraw.api.WithdrawFacade.schedulewithdraw")
@Slf4j
public class ScheduleFacadeImpl implements ScheduleCallBackService {
    @Autowired
    private TransactionWithdrawService withdrawTransaction;
    @Autowired
    private DistributeCacheService cacheService;
    @Autowired
    private TransactionWithdrawService transactionWithdrawService;
    @Autowired
    private ThreadPoolService threadPoolService;
    @Autowired
    private SendGateWayWithdrawStrategy sendGateWayWithdrawStrategy;

    public static final int PAGE_SIZE = 200;
    
    @Override
    public void justDoIT() {
        withdraw();
    }
    public void withdraw() {
        String gid = Ids.gid();//序列号
        //异步发送状态
        Set<TransStatusEnum> ASYN_SEND_TO_GATEWAY_STATUS = Sets.newHashSet(TransStatusEnum.INITIAL);
        //异步通知状态
        Set<NotifyStatusEnum> ASYN_NOT_NOTITY__STATUS = Sets.newHashSet(NotifyStatusEnum.DOING,NotifyStatusEnum.SUCCESS,NotifyStatusEnum.FAIL);
        synchronized (this){
            List<TransactionWithdraw> dbDoList;
            dbDoList = transactionWithdrawService.findAsynWithdrawSendToGateWay(ASYN_SEND_TO_GATEWAY_STATUS,ASYN_NOT_NOTITY__STATUS,PAGE_SIZE, ReviewStatusEnum.REVIEW_SUCCESS);
            if (CollectionUtils.isNotEmpty(dbDoList)) {
                log.info("提现序列号GID【{}】加载到[{}]条数据.....", gid,dbDoList.size());
                List<Long> ids = new ArrayList<>(dbDoList.size());
                for (TransactionWithdraw dbDo : dbDoList) {
                    ids.add(dbDo.getId());
                }
                log.info("提现序列号GID【{}】本次加载状态为[{}]流水共[{}]条记录,并将其状态设置为“SD”!",gid,ASYN_SEND_TO_GATEWAY_STATUS,ids.size());
                transactionWithdrawService.batchUpdateStatus(TransStatusEnum.DATA_LOCK, ids);
                List<TransactionWithdraw> lockedDbDoList = new LinkedList<>();
                //数据加锁
                for (TransactionWithdraw dbDo : dbDoList) {
                    try {
                        byte[] bytes = ( CacheConstant.LOCK_TYPE_WITHDRAW_SEND_GATEWAY+dbDo.getMerchOrderNo()).getBytes();
                        boolean islock = cacheService.lock(bytes,bytes,2*60);
                        if(islock) {
                            lockedDbDoList.add(dbDo);
                            log.info("提现序列号GID【{}】外部流水号【{}】内部流水号【{}】提现数据加锁成功key【{}】",gid,dbDo.getMerchOrderNo(),dbDo.getBizOrderNo(), CacheConstant.LOCK_TYPE_WITHDRAW_SEND_GATEWAY+dbDo.getPartnerId());
                        }
                        else {
                            log.info("提现序列号GID【{}】外部流水号【{}】内部流水号【{}】定时提现任务数据加锁异常",gid,dbDo.getMerchOrderNo(),dbDo.getBizOrderNo());
                            transactionWithdrawService.updateStatus(dbDo.getTransStatus(),dbDo.getBizOrderNo());
                        }
                    } catch (Exception e) {
                        log.error("提现序列号GID【{}】外部流水号【{}】内部流水号【{}】定时提现任务数据加锁异常",gid,dbDo.getMerchOrderNo(),dbDo.getBizOrderNo(),e);
                        transactionWithdrawService.updateStatus(dbDo.getTransStatus(),dbDo.getBizOrderNo());
                    }
                }
                if (CollectionUtils.isEmpty(lockedDbDoList)) {
                    log.info("提现序列号GID【{}】提现没有可处理的数据",gid);
                    return;
                }
                //发送提现数据
                CompletionService<String> completionService = new ExecutorCompletionService<>(threadPoolService);
                for (final TransactionWithdraw dbDo : lockedDbDoList) {
                    completionService.submit(() -> {
                        WithdrawDomain withdrawDomain = new WithdrawDomain();
                        WithdrawResult result = new WithdrawResult();
                        BeanCopier.copy(dbDo,withdrawDomain,BeanCopier.CopyStrategy.IGNORE_NULL, BeanCopier.NoMatchingRule.IGNORE,null);
                        sendGateWayWithdrawStrategy.doExcute(withdrawDomain,result);
                        //处理处理同步失败or同步提现成功状态,异步通知业务方 TODO
                        return dbDo.getBizOrderNo();
                    });
                }
                //获取异步返回结果
                for(int i=0 ;i < lockedDbDoList.size();i++){
                    String merchOrderNo = null;
                    try {
                        merchOrderNo = completionService.take().get();
                    } catch (Exception e) {
                        if(Strings.isNotEmpty(merchOrderNo)){
                            transactionWithdrawService.updateStatus(TransStatusEnum.GATEWAY_FAIL,merchOrderNo);
                        }
                        log.error("提现序列号GID【{}】执行调度任务发送网关异常", e);
                    } finally {
                        if(Strings.isNotEmpty(merchOrderNo)){
                            //释放锁
                            try {
                                cacheService.unlock(CacheConstant.LOCK_TYPE_WITHDRAW_SEND_GATEWAY+merchOrderNo);
                            } catch (Exception e) {
                                log.error("提现序列号GID【{}】CoreNo【{}】redis异常",gid,merchOrderNo,e);
                            }
                            log.info("提现序列号GID【{}】CoreNo【{}】提现数据解锁成功key【{}】",gid,merchOrderNo, CacheConstant.LOCK_TYPE_WITHDRAW_SEND_GATEWAY+merchOrderNo);
                        }
                    }
                }
            } else {
                log.info("提现序列号GID【{}】异步提现处理，未找到可执行数据",gid);
            }
        }
    }
}
